package com.niiwoo.civet.trade.service.local.flowexecute;

import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.rdb.sharding.api.HintManager;
import com.dangdang.ddframe.rdb.sharding.hint.HintManagerHolder;
import com.niiwoo.civet.trade.constant.LoanFlowMqConstant;
import com.niiwoo.civet.trade.dao.entity.ProjectFlowExecute;
import com.niiwoo.civet.trade.dao.entity.ProjectFlowExecuteRuntime;
import com.niiwoo.civet.trade.dao.entity.TianChengRequestRecord;
import com.niiwoo.civet.trade.dao.mapper.ProjectFlowExecuteMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectFlowExecuteRuntimeMapperExt;
import com.niiwoo.civet.trade.dao.mapper.TianChengRequestRecordMapperExt;
import com.niiwoo.civet.trade.dto.request.loan.ProjectFlowExecuteRuntimeDTO;
import com.niiwoo.civet.trade.dto.request.loan.TianChengRequestRecordDTO;
import com.niiwoo.civet.trade.enums.LoanNodeTypeEnum;
import com.niiwoo.civet.trade.enums.ProjectFlowExecuteRuntimeNodeStatusEnum;
import com.niiwoo.civet.trade.enums.ProjectFlowExecuteStatusEnum;
import com.niiwoo.civet.trade.enums.ProjectTypeEnum;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import com.niiwoo.tripod.tiancheng.enums.FunctionCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author wping
 * @Description: 标的申请流程控制服务类
 * @date 2018/4/19 10:14
 */
@Slf4j
@Service
public class ProjectFlowExecuteService {

    @Autowired
    private ProjectFlowExecuteMapperExt projectFlowExecuteMapperExt;

    @Autowired
    private ProjectFlowExecuteRuntimeMapperExt projectFlowExecuteRuntimeMapperExt;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private TianChengRequestRecordMapperExt tianChengRequestRecordMapperExt;

    /**
     * 发送流程执行MQ
     * @param flowExecuteId 流程执行ID
     * @param queue MQ队列，请参考LoanFlowMqConstant.Queue
     */
    public void sendLoanFlowMq(Long flowExecuteId, String queue){
        JSONObject json = new JSONObject();
        json.put("flowExecuteId", flowExecuteId);
        rabbitTemplate.convertAndSend(LoanFlowMqConstant.Exchange.LOAN_FLOW_EXCHANGE, LoanFlowMqConstant.getRoutingKey(LoanFlowMqConstant.Exchange.LOAN_FLOW_EXCHANGE, queue), json);
        log.info("发送借款流程MQ成功，flowExecuteId:{}, queue:{}", flowExecuteId, queue);
    }

    /**
     * 初始化流程第一步执行节点为处理中(初始化借款相关数据回流，仅供提交借款申请、退回标的提交借款申请调用)
     * @param projectId 标的ID(必传)
     * @param borrowUserId 用户ID(必传)
     * @param projectTypeEnum 标的类型，参考ProjectTypeEnum
     * @return flowExecuteId 成功时返回标的申请流程总概表-主键ID，失败返回null
     */
    @Transactional(rollbackFor = Exception.class)
    public Long initFirstStepProjectFlowExecute (Long projectId, String borrowUserId, ProjectTypeEnum projectTypeEnum) {

        if (projectId == null || StringUtils.isEmpty(borrowUserId) || projectTypeEnum == null){
            log.error("初始化标的申请流程-失败，projectId/borrowUserId/projectTypeEnum非空");
            return null;
        }

        Long flowExecuteId = snowflakeIdWorker.nextId();
        String methodName = "初始化标的申请流程，projectId:" + projectId + ", borrowUserId:" + borrowUserId + ", flowExecuteId:" + flowExecuteId + ", projectTypeEnum" + projectTypeEnum.getDesc();

        Date now = new Date();
        ProjectFlowExecute projectFlowExecute = new ProjectFlowExecute();
        projectFlowExecute.setFlowExecuteId(flowExecuteId);
        projectFlowExecute.setProjectType(projectTypeEnum.getValue());
        projectFlowExecute.setProjectId(projectId);
        projectFlowExecute.setBorrowUserId(borrowUserId);
        projectFlowExecute.setEndNodeId(LoanNodeTypeEnum.LOAN_DATA_BACK_FLOW.getId());
        projectFlowExecute.setBeginTime(now);
        projectFlowExecute.setStatus(ProjectFlowExecuteStatusEnum.EXECUTING.getStatus());
        projectFlowExecute.setDesc(ProjectFlowExecuteStatusEnum.EXECUTING.getDesc());
        projectFlowExecute.setCreateTime(now);
        projectFlowExecute.setUpdateTime(now);

        projectFlowExecuteMapperExt.insertSelective(projectFlowExecute);

        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = new ProjectFlowExecuteRuntime();
        projectFlowExecuteRuntime.setFlowExecuteId(flowExecuteId);
        projectFlowExecuteRuntime.setProjectId(projectId);
        projectFlowExecuteRuntime.setProjectType(projectTypeEnum.getValue());
        projectFlowExecuteRuntime.setBorrowUserId(borrowUserId);
        projectFlowExecuteRuntime.setNodeId(LoanNodeTypeEnum.LOAN_DATA_BACK_FLOW.getId());
        projectFlowExecuteRuntime.setNodeStatus(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getStatus());
        projectFlowExecuteRuntime.setErrMsg(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getDesc());
        projectFlowExecuteRuntime.setRetryTimes(0);
        projectFlowExecuteRuntime.setCreateTime(now);
        projectFlowExecuteRuntime.setUpdateTime(now);

        projectFlowExecuteRuntimeMapperExt.insertSelective(projectFlowExecuteRuntime);

        log.info(methodName + "-插入成功");

        // 发送MQ
        sendLoanFlowMq(flowExecuteId, LoanFlowMqConstant.Queue.LOAN_DATA_BACK_FLOW_QUEUE);

        return flowExecuteId;

    }

    @Transactional(rollbackFor = Exception.class)
    public Long initTeAmoutSubmitFirstStepExecute (Long projectId, String borrowUserId, ProjectTypeEnum projectTypeEnum) {

        if (projectId == null || StringUtils.isEmpty(borrowUserId) || projectTypeEnum == null){
            log.error("初始化提额认证标的申请流程-失败，projectId/borrowUserId/projectTypeEnum非空");
            return null;
        }

        Long flowExecuteId = snowflakeIdWorker.nextId();
        String methodName = "初始化提额认证标的申请流程，projectId:" + projectId + ", borrowUserId:" + borrowUserId + ", flowExecuteId:" + flowExecuteId + ", projectTypeEnum" + projectTypeEnum.getDesc();

        Date now = new Date();
        ProjectFlowExecute projectFlowExecute = new ProjectFlowExecute();
        projectFlowExecute.setFlowExecuteId(flowExecuteId);
        projectFlowExecute.setProjectType(projectTypeEnum.getValue());
        projectFlowExecute.setProjectId(projectId);
        projectFlowExecute.setBorrowUserId(borrowUserId);
        projectFlowExecute.setEndNodeId(LoanNodeTypeEnum.LOAN_TE_LIMIT_SCORE.getId());
        projectFlowExecute.setBeginTime(now);
        projectFlowExecute.setStatus(ProjectFlowExecuteStatusEnum.EXECUTING.getStatus());
        projectFlowExecute.setDesc(ProjectFlowExecuteStatusEnum.EXECUTING.getDesc());
        projectFlowExecute.setCreateTime(now);
        projectFlowExecute.setUpdateTime(now);

        projectFlowExecuteMapperExt.insertSelective(projectFlowExecute);

        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = new ProjectFlowExecuteRuntime();
        projectFlowExecuteRuntime.setFlowExecuteId(flowExecuteId);
        projectFlowExecuteRuntime.setProjectId(projectId);
        projectFlowExecuteRuntime.setProjectType(projectTypeEnum.getValue());
        projectFlowExecuteRuntime.setBorrowUserId(borrowUserId);
        projectFlowExecuteRuntime.setNodeId(LoanNodeTypeEnum.LOAN_TE_LIMIT_SCORE.getId());
        projectFlowExecuteRuntime.setNodeStatus(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getStatus());
        projectFlowExecuteRuntime.setErrMsg(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getDesc());
        projectFlowExecuteRuntime.setRetryTimes(0);
        projectFlowExecuteRuntime.setCreateTime(now);
        projectFlowExecuteRuntime.setUpdateTime(now);

        projectFlowExecuteRuntimeMapperExt.insertSelective(projectFlowExecuteRuntime);

        log.info(methodName + "-插入成功");

        // 发送MQ
        sendLoanFlowMq(flowExecuteId, LoanFlowMqConstant.Queue.LOAN_TE_LIMIT_SCORE_QUEUE);

        return flowExecuteId;

    }

    /**
     * ProjectFlowExecuteRuntime.nodeId节点为当前执行节点(currentStepNodeTypeEnum)时，初始化下一步执行节点(nextStepNodeTypeEnum)流程为处理中
     * @param flowExecuteId 流程处理主键ID
     * @param currentStepNodeTypeEnum 当前执行节点
     * @param nextStepNodeTypeEnum 下一步执行节点
     * @param nextMqQueue 下一步mq队列, 请参考LoanFlowMqConstant.Queue
     * @return 成功返回true, 否则false
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean initNextStepProjectFlowExecute (Long flowExecuteId, LoanNodeTypeEnum currentStepNodeTypeEnum, LoanNodeTypeEnum nextStepNodeTypeEnum, String nextMqQueue) {

        if (flowExecuteId == null || currentStepNodeTypeEnum == null || nextStepNodeTypeEnum == null){
            log.error("初始化下一步标的申请流程-失败，flowExecuteId/currentStepNodeTypeEnum/nodeTypeEnum非空");
            return false;
        }

        String methodName = "初始化下一步标的申请流程，flowExecuteId:" + flowExecuteId + ", currentStepNodeTypeEnum:" + currentStepNodeTypeEnum.getDesc() + ", nextStepNodeTypeEnum:" + nextStepNodeTypeEnum.getDesc();
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecute projectFlowExecute = projectFlowExecuteMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecute == null) {
            log.error(methodName + "-失败，ProjectFlowExecute记录不存在");
            return false;
        }

        if (projectFlowExecute.getStatus() != null && ProjectFlowExecuteStatusEnum.EXECUTING.getStatus() != projectFlowExecute.getStatus().byteValue()) {
            log.info(methodName + "-流程已经结束，退出不更新");
            return false;
        }

        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecuteRuntime == null) {
            log.error(methodName + "-失败，ProjectFlowExecuteRuntime记录不存在");
            return false;
        }

//        if (currentStepNodeTypeEnum.getId() != projectFlowExecuteRuntime.getNodeId().intValue()) {
//            log.error(methodName + "-失败，当前节点不一致，ProjectFlowExecuteRuntime.nodeId:{}", projectFlowExecuteRuntime.getNodeId());
//            return false;
//        }

        Date now = new Date();
        ProjectFlowExecuteRuntime updateProjectFlowExecuteRuntime = new ProjectFlowExecuteRuntime();
        updateProjectFlowExecuteRuntime.setFlowExecuteId(flowExecuteId);
        updateProjectFlowExecuteRuntime.setNodeId(nextStepNodeTypeEnum.getId());
        updateProjectFlowExecuteRuntime.setNodeStatus(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getStatus());
        updateProjectFlowExecuteRuntime.setErrMsg(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getDesc());
        updateProjectFlowExecuteRuntime.setRetryTimes(0);
        updateProjectFlowExecuteRuntime.setCreateTime(now);
        updateProjectFlowExecuteRuntime.setUpdateTime(now);
        projectFlowExecuteRuntimeMapperExt.updateByPrimaryKeySelective(updateProjectFlowExecuteRuntime);

        ProjectFlowExecute updateProjectFlowExecute = new ProjectFlowExecute();
        updateProjectFlowExecute.setFlowExecuteId(flowExecuteId);
        updateProjectFlowExecute.setEndNodeId(nextStepNodeTypeEnum.getId());
        updateProjectFlowExecute.setUpdateTime(now);
        projectFlowExecuteMapperExt.updateByPrimaryKeySelective(updateProjectFlowExecute);

        log.info(methodName + ", projectId:" + projectFlowExecute.getProjectId() + ", borrowUserId" + projectFlowExecute.getBorrowUserId() + "-成功");

        // 发送MQ
        if(nextMqQueue != null) {
            sendLoanFlowMq(flowExecuteId, nextMqQueue);
        }

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean initNextStepProjectTianchengRequest(Long flowExecuteId, LoanNodeTypeEnum currentStepNodeTypeEnum, LoanNodeTypeEnum nextStepNodeTypeEnum,
                                                       String nextMqQueue, String orderId, FunctionCodeEnum functionCodeEnum, String userId, Long projectId) {

        if (flowExecuteId == null || currentStepNodeTypeEnum == null || nextStepNodeTypeEnum == null) {
            log.error("初始化下一步标的申请流程-失败，flowExecuteId/currentStepNodeTypeEnum/nodeTypeEnum非空");
            return false;
        }

        String methodName = "初始化下一步标的申请流程，flowExecuteId:" + flowExecuteId + ", currentStepNodeTypeEnum:" + currentStepNodeTypeEnum.getDesc() + ", nextStepNodeTypeEnum:" + nextStepNodeTypeEnum.getDesc();
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecute projectFlowExecute = projectFlowExecuteMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecute == null) {
            log.error(methodName + "-失败，ProjectFlowExecute记录不存在");
            return false;
        }

        if (projectFlowExecute.getStatus() != null && ProjectFlowExecuteStatusEnum.EXECUTING.getStatus() != projectFlowExecute.getStatus().byteValue()) {
            log.info(methodName + "-流程已经结束，退出不更新");
            return false;
        }

        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecuteRuntime == null) {
            log.error(methodName + "-失败，ProjectFlowExecuteRuntime记录不存在");
            return false;
        }

//        if (currentStepNodeTypeEnum.getId() != projectFlowExecuteRuntime.getNodeId().intValue()) {
//            log.error(methodName + "-失败，当前节点不一致，ProjectFlowExecuteRuntime.nodeId:{}", projectFlowExecuteRuntime.getNodeId());
//            return false;
//        }

        Date now = new Date();
        ProjectFlowExecuteRuntime updateProjectFlowExecuteRuntime = new ProjectFlowExecuteRuntime();
        updateProjectFlowExecuteRuntime.setFlowExecuteId(flowExecuteId);
        updateProjectFlowExecuteRuntime.setNodeId(nextStepNodeTypeEnum.getId());
        updateProjectFlowExecuteRuntime.setNodeStatus(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getStatus());
        updateProjectFlowExecuteRuntime.setErrMsg(ProjectFlowExecuteRuntimeNodeStatusEnum.HANDING.getDesc());
        updateProjectFlowExecuteRuntime.setRetryTimes(0);
        updateProjectFlowExecuteRuntime.setCreateTime(now);
        updateProjectFlowExecuteRuntime.setUpdateTime(now);
        projectFlowExecuteRuntimeMapperExt.updateByPrimaryKeySelective(updateProjectFlowExecuteRuntime);

        ProjectFlowExecute updateProjectFlowExecute = new ProjectFlowExecute();
        updateProjectFlowExecute.setFlowExecuteId(flowExecuteId);
        updateProjectFlowExecute.setEndNodeId(nextStepNodeTypeEnum.getId());
        updateProjectFlowExecute.setUpdateTime(now);
        projectFlowExecuteMapperExt.updateByPrimaryKeySelective(updateProjectFlowExecute);

        log.info(methodName + ", projectId:" + projectFlowExecute.getProjectId() + ", borrowUserId" + projectFlowExecute.getBorrowUserId() + "-成功");

        // 发送MQ
        if (nextMqQueue != null) {
            sendLoanFlowMq(flowExecuteId, nextMqQueue);
        }

        //放在一个事务里面进行
        TianChengRequestRecordDTO tianChengRequestRecordDTO = new TianChengRequestRecordDTO();
        tianChengRequestRecordDTO.setBorrowUserId(userId);
        tianChengRequestRecordDTO.setFlowExecuteId(flowExecuteId);
        tianChengRequestRecordDTO.setFunctionCode(functionCodeEnum.getHeadFunctionCode());
        tianChengRequestRecordDTO.setOrderId(orderId);
        tianChengRequestRecordDTO.setProjectId(projectId);

        TianChengRequestRecord record = new TianChengRequestRecord();
        BeanUtils.copyProperties(tianChengRequestRecordDTO, record);
        record.setId(snowflakeIdWorker.nextId());
        record.setCreateTime(new Date());
        tianChengRequestRecordMapperExt.insertSelective(record);
        return true;
    }

    /**
     * 更新当前节点执行结果状态（执行异常时调用更新节点状态）
     * @param flowExecuteId 流程处理主键ID
     * @param nodeTypeEnum 当前执行节点
     * @param runtimeNodeStatusEnum 当前执行节点执行结果状态
     * @param errMsg 当前执行节点执行结果描述
     * @return 成功返回true, 否则返回false
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProjectFlowExecuteRuntime (Long flowExecuteId, LoanNodeTypeEnum nodeTypeEnum, ProjectFlowExecuteRuntimeNodeStatusEnum runtimeNodeStatusEnum, String errMsg){
        return updateProjectFlowExecuteRuntime (flowExecuteId, nodeTypeEnum, runtimeNodeStatusEnum, errMsg, -1);
    }


    /**
     * 更新当前节点执行结果状态（执行异常时调用更新节点状态）
     * @param flowExecuteId 流程处理主键ID
     * @param nodeTypeEnum 当前执行节点
     * @param runtimeNodeStatusEnum 当前执行节点执行结果状态
     * @param errMsg 当前执行节点执行结果描述
     * @param retryTimes 重试次数(大于0时有效，否则自增)
     * @return 成功返回true, 否则返回false
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProjectFlowExecuteRuntime (Long flowExecuteId, LoanNodeTypeEnum nodeTypeEnum, ProjectFlowExecuteRuntimeNodeStatusEnum runtimeNodeStatusEnum, String errMsg, int retryTimes) {

        if (flowExecuteId == null || nodeTypeEnum == null || runtimeNodeStatusEnum == null){
            log.error("更新标的申请流程执行表-失败，flowExecuteId/nodeTypeEnum/runtimeNodeStatusEnum非空");
            return false;
        }

        String methodName = "更新标的申请流程执行表，flowExecuteId:" + flowExecuteId + ", nodeTypeEnum:" + nodeTypeEnum.getDesc() + ", runtimeNodeStatusEnum:" + runtimeNodeStatusEnum.getDesc();
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecute projectFlowExecute = projectFlowExecuteMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecute == null) {
            log.error(methodName + "-失败，ProjectFlowExecute记录不存在");
            return false;
        }

        if (projectFlowExecute.getStatus() != null && ProjectFlowExecuteStatusEnum.EXECUTING.getStatus() != projectFlowExecute.getStatus().byteValue()) {
            log.info(methodName + "-流程已经结束，退出不更新");
            return false;
        }

        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecuteRuntime == null) {
            log.error(methodName + "-失败，ProjectFlowExecuteRuntime记录不存在");
            return false;
        }

//        if (nodeTypeEnum.getId() != projectFlowExecuteRuntime.getNodeId().intValue()) {
//            log.error(methodName + "-失败，当前节点不一致，ProjectFlowExecuteRuntime.nodeId:{}", projectFlowExecuteRuntime.getNodeId());
//            return false;
//        }

        Date now = new Date();
        ProjectFlowExecuteRuntime updateProjectFlowExecuteRuntime = new ProjectFlowExecuteRuntime();
        updateProjectFlowExecuteRuntime.setFlowExecuteId(flowExecuteId);
        updateProjectFlowExecuteRuntime.setNodeStatus(runtimeNodeStatusEnum.getStatus());
        updateProjectFlowExecuteRuntime.setErrMsg(subString(errMsg, 256));
        if (retryTimes > 0) {
            updateProjectFlowExecuteRuntime.setRetryTimes(retryTimes);
        } else {
            updateProjectFlowExecuteRuntime.setRetryTimes(projectFlowExecuteRuntime.getRetryTimes() + 1);
        }
        updateProjectFlowExecuteRuntime.setUpdateTime(now);

        projectFlowExecuteRuntimeMapperExt.updateByPrimaryKeySelective(updateProjectFlowExecuteRuntime);

        log.info(methodName + ", projectId:" + projectFlowExecute.getProjectId() + ", borrowUserId" + projectFlowExecute.getBorrowUserId() + "-成功");

        return true;
    }

    /**
     * 成功结束标的申请流程(借款流程最后一步执行成功调用：i.e. PROJECT_TO_BANK(120, "上标(自动审核开关开启)")
     * @param flowExecuteId 流程处理主键ID
     * @param endNodeTypeEnum 结束整个流程的节点
     * @return 成功返回true, 失败返回false
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean successEndProjectFlowExecute (Long flowExecuteId, LoanNodeTypeEnum endNodeTypeEnum) {
        return endProjectFlowExecute (flowExecuteId, endNodeTypeEnum, true, "成功");
    }

    /**
     * 失败结束整个标的申请流程
     * @param flowExecuteId 流程处理主键ID
     * @param endNodeTypeEnum 结束整个流程的节点
     * @param errMsg 失败描述
     * @return 成功返回true, 失败返回false
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean failEndProjectFlowExecute (Long flowExecuteId, LoanNodeTypeEnum endNodeTypeEnum, String errMsg) {
        return endProjectFlowExecute (flowExecuteId, endNodeTypeEnum, false, errMsg);
    }

    private boolean endProjectFlowExecute (Long flowExecuteId, LoanNodeTypeEnum endNodeTypeEnum, boolean isSuccess, String errMsg) {

        if (flowExecuteId == null || endNodeTypeEnum == null){
            log.error("结束标的申请流程-失败，flowExecuteId/endNodeTypeEnum非空");
            return false;
        }

        String methodName = "结束标的申请流程，flowExecuteId:" + flowExecuteId + ", endNodeTypeEnum:" + endNodeTypeEnum.getDesc() + ", isSuccess:" + isSuccess + ", errMsg:" + errMsg;
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecute projectFlowExecute = projectFlowExecuteMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecute == null) {
            log.error(methodName + "-失败，ProjectFlowExecute记录不存在");
            return false;
        }

        if (projectFlowExecute.getStatus() != null && ProjectFlowExecuteStatusEnum.EXECUTING.getStatus() != projectFlowExecute.getStatus().byteValue()) {
            log.info(methodName + "-流程已经结束，退出不更新");
            return false;
        }

        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecuteRuntime == null) {
            log.error(methodName + "-失败，ProjectFlowExecuteRuntime记录不存在");
            return false;
        }

//        if (endNodeTypeEnum.getId() != projectFlowExecuteRuntime.getNodeId().intValue()) {
//            log.error(methodName + "-失败，当前节点不一致，ProjectFlowExecuteRuntime.nodeId:{}", projectFlowExecuteRuntime.getNodeId());
//            return false;
//        }
        // 删除明细记录
        projectFlowExecuteRuntimeMapperExt.deleteByPrimaryKey(flowExecuteId);

        Date now = new Date();
        ProjectFlowExecute updateProjectFlowExecute = new ProjectFlowExecute();
        updateProjectFlowExecute.setFlowExecuteId(flowExecuteId);
        updateProjectFlowExecute.setEndNodeId(endNodeTypeEnum.getId());
        updateProjectFlowExecute.setEndTime(now);
        updateProjectFlowExecute.setStatus(isSuccess ? ProjectFlowExecuteStatusEnum.SUCCESS.getStatus() : ProjectFlowExecuteStatusEnum.FAIL.getStatus());
        updateProjectFlowExecute.setDesc(subString(errMsg, 256));
        updateProjectFlowExecute.setUpdateTime(now);

        projectFlowExecuteMapperExt.updateByPrimaryKeySelective(updateProjectFlowExecute);

        log.info(methodName + ", projectId:" + projectFlowExecute.getProjectId() + ", borrowUserId:" + projectFlowExecute.getBorrowUserId() + "-成功");

        return true;
    }

    // 截图指定的前字符串长度
    private String subString(String value, int maxLength) {
        if (StringUtils.isNotEmpty(value)) {
            if (value.length() > maxLength) {
                return value.substring(0, maxLength);
            } else {
                return value;
            }
        }
        return null;
    }

    /**
     * 通过节点id和状态查找标的申请流程执行表
     * @return
     */
    public List<ProjectFlowExecuteRuntimeDTO> queryByStatusNode(int nodeId,Byte nodeStatus){
        List<ProjectFlowExecuteRuntime> list = projectFlowExecuteRuntimeMapperExt.queryByStatusANode(nodeId,nodeStatus);
        return transform(list);
    }

    /**
     * 按超时时间 通过节点id和状态查找标的申请流程执行表
     * @param nodeId
     * @param nodeStatus
     * @param timeoutMinute  超时分钟
     * @return
     */
    public List<ProjectFlowExecuteRuntimeDTO> queryByStatusNode(int nodeId, Byte nodeStatus, int timeoutMinute){
        List<ProjectFlowExecuteRuntime> list = projectFlowExecuteRuntimeMapperExt.queryByStatusANodeAtimeout(nodeId, nodeStatus, timeoutMinute);
        return transform(list);
    }

    /**
     * 对象转换
     * @param projectFlowExecuteRuntimes
     * @return
     */
    private List<ProjectFlowExecuteRuntimeDTO> transform(List<ProjectFlowExecuteRuntime> projectFlowExecuteRuntimes){
        List<ProjectFlowExecuteRuntimeDTO> projectFlowExecuteRuntimeDTOS = null;
        if(!projectFlowExecuteRuntimes.isEmpty()){
            for(ProjectFlowExecuteRuntime projectFlowExecuteRuntime : projectFlowExecuteRuntimes){
                ProjectFlowExecuteRuntimeDTO projectFlowExecuteRuntimeDTO = new ProjectFlowExecuteRuntimeDTO();
                BeanUtils.copyProperties(projectFlowExecuteRuntime, projectFlowExecuteRuntimeDTO);
                projectFlowExecuteRuntimeDTOS = new ArrayList<>();
                projectFlowExecuteRuntimeDTOS.add(projectFlowExecuteRuntimeDTO);
            }
        }
        return projectFlowExecuteRuntimeDTOS;
    }
}
